package com.wnxy.d_syn;

/**
 * synchronized 同步锁; 重量级锁
 * 1、使用了synchronized同步代码块，就会对同步代码块资源加锁，只有获取锁的线程才能够访问；
 * 2、获取不到锁的线程，需要等待；
 * 3、等到synchronized同步代码块执行完毕，锁会自动释放
 * 4、所以：synchronized可以解决多线程访问共享资源线程安全问题
 *
 * synchronized应用：
 * 1、同步代码块： 获取锁的线程才可以执行，执行完执行释放
 *    synchronized (TicketDemo2.class) { }
 * 2、修饰方法
 *    使用1：对当前实例对象加锁，多线程操作同一个对象实例的时候才进行线程同步
 *          public synchronized void method() {}
 *    使用2：对静态方法加锁; 相当于对类加锁，多线程操作当前类的所有实例对象都进行线程同步
 *          public static synchronized void method(){}
 *
 *  哪些对象可以作为锁？
 *     任意对象。
 *  使用synchronized锁对象时候，要注意什么？
 *     多线程并行访问同步代码块，需要锁同一个对象，才可以进行线程同步。
 *  synchronized 作用？
 *      获得锁的线程才可以进入同步代码块，没有获取锁的排队等待。
 */
public class TicketDemo2 {
    // 总票数
    private static int ticket = 50;

    public static void main(String[] args) {

        Object obj = new Object();

        Runnable runnable = ()-> {
            while (true) {
                // 这里休眠一下，就能模拟出超卖现象
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // synchronized 同步代码块，解决线程安全问题
                synchronized (obj) {
                    if (ticket > 0) {
                        ticket--;
                        System.out.println(Thread.currentThread().getName() +
                                "卖了一张票，剩余：" + ticket);
                    } else {
                        // 票已卖完
                        break;
                    }
                }
            }
        };

        // 模拟三个售票窗口, 用三个线程表示
        Thread t1 = new Thread(runnable,"售票窗口1");
        Thread t2 = new Thread(runnable,"售票窗口2");
        Thread t3 = new Thread(runnable,"售票窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
}
